package com.gjd.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gjd.domain.ResponseResult;
import com.gjd.domain.dto.ManageEmployerDto;
import com.gjd.domain.entity.*;
import com.gjd.utils.RedisCache;
import com.gjd.domain.vo.PageVo;
import com.gjd.domain.vo.UserInfoDetailVo;
import com.gjd.domain.vo.UserInfoVo;
import com.gjd.domain.vo.enums.AppHttpCodeEnum;
import com.gjd.exception.SystemException;
import com.gjd.mapper.UserMapper;
import com.gjd.service.CategoryService;
import com.gjd.service.ServerConditionService;
import com.gjd.service.UserService;
import com.gjd.utils.*;
import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import io.jsonwebtoken.Claims;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

@Service
@Data
@ConfigurationProperties(prefix = "oss")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private ServerConditionService serverConditionService;
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private RedisCache redisCache;

    @Override
    public ResponseResult register(User user) {
//        对数据进行非空判断
//        hasText返回值：true（非空），false（空）
        if (!StringUtils.hasText(user.getUserName())) {
            throw new SystemException(AppHttpCodeEnum.USERNAME_NOT_NULL);
        }
        if (!StringUtils.hasText(user.getPassword())) {
            throw new SystemException(AppHttpCodeEnum.PASSWORD_NOT_NULL);
        }
        if (!StringUtils.hasText(user.getEmail())) {
            throw new SystemException(AppHttpCodeEnum.EMAIL_NOT_NULL);
        }
        if (!StringUtils.hasText(user.getUserType())) {
            throw new SystemException(AppHttpCodeEnum.USERTYPE_NOT_NULL);
        }
//        不为空则进行是否重复的判断
        if (userNameExist(user.getUserName())) {
            throw new SystemException(AppHttpCodeEnum.USERNAME_EXIST);
        }
        if (userEmailExist(user.getEmail())) {
            throw new SystemException(AppHttpCodeEnum.EMAIL_EXIST);
        }
//        对合理的数据进行存储，密码加密存储
        String encodePassword = passwordEncoder.encode(user.getPassword());
        user.setPassword(encodePassword);
//        TODO 后期可根据usertype的不同插入在不同表中
        userMapper.insert(user);
//        返回提示信息
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult getServerList(Long categoryId, Integer pageNum, Integer pageSize) {
        //查询条件
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        //如果有typeid，就需要查询与传入一致的
        if (Objects.nonNull(categoryId) && categoryId > 0) {
            queryWrapper.eq(User::getCategoryId, categoryId);
        }
        //状态正常
        queryWrapper.eq(User::getStatus, 0);
        queryWrapper.orderByAsc(User::getVip);

        //TODO: 分页查询
        Page<User> page = new Page<>(pageNum, pageSize);
        Page<User> selectPage = userMapper.selectPage(page, queryWrapper);

        //封装查询结果（PageVo）
        List<UserInfoVo> userInfoVoList = BeanCopyUtils.copyBeanList(selectPage.getRecords(), UserInfoVo.class);
        PageVo pageVo = new PageVo(userInfoVoList, selectPage.getTotal());

        return ResponseResult.okResult(pageVo);
    }

    @Override
    public ResponseResult getuserInfo() {
        //根据token获取当前用户id
        Long userId = SecurityUtils.getUserId();
        //根据用户id查询信息
        //TODO: getById(userId)是继承自baseMapper才有的方法，IService没有
        User user = userMapper.selectById(userId);
        //封装为UserInfoVo
        UserInfoVo userInfoVo = BeanCopyUtils.copyBean(user, UserInfoVo.class);
        return ResponseResult.okResult(userInfoVo);
    }

    @Override
    public ResponseResult cheackUserInfo(Long serverId) {
        User user = userMapper.selectById(serverId);
        LambdaQueryWrapper<ServerCondition> serverConditionLambdaQueryWrapper = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<Category> categoryLambdaQueryWrapper = new LambdaQueryWrapper<>();
        serverConditionLambdaQueryWrapper.eq(ServerCondition::getServerId, serverId);
        ServerCondition serverCondition = (ServerCondition) serverConditionService.getCondition(serverId).getData();
        Integer categoryId = serverCondition.getCategoryId();
        categoryLambdaQueryWrapper.eq(Category::getId, categoryId);
        Category category = null;
        try {
//            TODO: 管理员管理分类列表--联级删除
            category = categoryService.getOne(categoryLambdaQueryWrapper);
        } catch (Exception e) {
            System.out.println("该分类不存在");
            String categoryName = null;
        }
        String categoryName = category.getName();

        //封装
//        TODO：住家和长期
        UserInfoVo userInfoVo = BeanCopyUtils.copyBean(user, UserInfoVo.class);
        UserInfoDetailVo userInfoDetailVo = BeanCopyUtils.copyBean(userInfoVo, UserInfoDetailVo.class);
        userInfoDetailVo.setCategoryName(categoryName);
        userInfoDetailVo.setTreatment(serverCondition.getTreatment());
        userInfoDetailVo.setServerTime(serverCondition.getServerTime());
        userInfoDetailVo.setServerType(serverCondition.getServerType());
        if (serverCondition.getStay() == 1) {
            userInfoDetailVo.setStay("住家");
        } else if (serverCondition.getStay() == 2) {
            userInfoDetailVo.setStay("不住家");
        }
        return ResponseResult.okResult(userInfoDetailVo);
    }

    @Override
    public ResponseResult uploadImg(MultipartFile img) {
        //判断文件类型和大小
        //获取原始文件名
        String filename = img.getOriginalFilename();
        if (!filename.endsWith(".png") && !filename.endsWith(".jpg")) {
            return ResponseResult.errorResult(AppHttpCodeEnum.FILE_TYPE_ERROR);
        }
        //通过则上传到oss
        String filePath = PathUtils.generateFilePath(filename);
        User user = SecurityUtils.getLoginUser().getUser();
        String url = uploadOss(img, filePath);
        user.setAvatar(url);
        //数据库更新
        userMapper.updateById(user);
        return ResponseResult.okResult(url);
    }

    @Override
    public ResponseResult updateUserInfo(User user) {
        Long userId = SecurityUtils.getUserId();
        user.setId(userId);
        int res = userMapper.updateById(user);
        if (res == 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
        }
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult updatePassword(Passwords passwords) {
        User user = SecurityUtils.getLoginUser().getUser();
        //TODO: 明文与密文的校对
        String oldPW = passwords.getOldPW();
        String newPW = passwords.getNewPW();
        String dbPassword = SecurityUtils.getLoginUser().getPassword();
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        if (!passwordEncoder.matches(oldPW, dbPassword)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PASSWORD_ERROR);
        }
        String encode = passwordEncoder.encode(newPW);
        user.setPassword(encode);
        userMapper.updateById(user);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult getUserListPage(Long pageNum, Long pageSize) {
        Page<User> page = new Page<>(pageNum, pageSize);
        Page<User> selectPage = userMapper.selectPage(page, null);
        List<UserInfoVo> userInfoVos = BeanCopyUtils.copyBeanList(selectPage.getRecords(), UserInfoVo.class);
        List<ManageEmployerDto> manageEmployerDtoList = BeanCopyUtils.copyBeanList(userInfoVos, ManageEmployerDto.class);
        PageVo pageVo = new PageVo(manageEmployerDtoList, selectPage.getTotal());
        return ResponseResult.okResult(pageVo);
    }

    @Override
    public ResponseResult selectUserListPage(User user, Long pageNum, Long pageSize) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(user.getNickName() != null, User::getNickName, user.getNickName());
        queryWrapper.eq(user.getPhoneNumber() != null, User::getPhoneNumber, user.getPhoneNumber());
        queryWrapper.eq(user.getSex() != null, User::getSex, user.getSex());
        Page<User> page = new Page<>(pageNum, pageSize);
        Page<User> selectPage = userMapper.selectPage(page, queryWrapper);
        if (selectPage.getRecords().size() != 0) {
            List<UserInfoVo> userInfoVos = BeanCopyUtils.copyBeanList(selectPage.getRecords(), UserInfoVo.class);
            return ResponseResult.okResult(userInfoVos);
        }
        return ResponseResult.errorResult(AppHttpCodeEnum.USERNAME_NO_EXIST);
    }

    @Override
    public ResponseResult deleteUser(Long userId) {
        userMapper.deleteById(userId);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult checkToken(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        {
            //获取请求头中的token
            String token = request.getHeader("token");
            Claims claims = null;
            try {
                claims = JwtUtil.parseJWT(token);
            } catch (Exception e) {
                e.printStackTrace();
                //token超时  token非法
                //响应告诉前端需要重新登录
                ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
                WebUtils.renderString(response, JSON.toJSONString(result));
                return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
            }
            String userId = claims.getSubject();

            //从redis中获取用户信息
            LoginUser loginUser = redisCache.getCacheObject("loginUser:" + userId);
            //如果获取不到
            if (Objects.isNull(loginUser)) {
                //说明登录过期  提示重新登录
                ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
                WebUtils.renderString(response, JSON.toJSONString(result));
            }
            return ResponseResult.okResult();
        }
    }

    private String accessKey;
    private String secretKey;
    private String bucket;

    public void setAccessKey(String accessKey) {
        this.accessKey = accessKey;
    }

    public void setSecretKey(String secretKey) {
        this.secretKey = secretKey;
    }

    public void setBucket(String bucket) {
        this.bucket = bucket;
    }

    private String uploadOss(MultipartFile imgFile, String filePath) {
        //构造一个带指定 Region 对象的配置类
        Configuration cfg = new Configuration(Region.autoRegion());
        cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;// 指定分片上传版本
        //...其他参数参考类注释

        UploadManager uploadManager = new UploadManager(cfg);

        //默认不指定key的情况下，以文件内容的hash值作为文件名
        String key = filePath;

        try {
            InputStream inputStream = imgFile.getInputStream();
            Auth auth = Auth.create(accessKey, secretKey);
            String upToken = auth.uploadToken(bucket);

            try {
                Response response = uploadManager.put(inputStream, key, upToken, null, null);
                //解析上传成功的结果
                DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
                System.out.println(putRet.key);
                System.out.println(putRet.hash);
                return "http://ro76gtst3.hn-bkt.clouddn.com/" + key;
            } catch (QiniuException ex) {
                Response r = ex.response;
                System.err.println(r.toString());
                try {
                    System.err.println(r.bodyString());
                } catch (QiniuException ex2) {
                    //ignore
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return "www";
    }

    private boolean userEmailExist(String email) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        Integer rs = userMapper.selectCount(wrapper.eq(User::getEmail, email));
        return (rs > 0);
    }

    private boolean userNameExist(String userName) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        Integer rs = userMapper.selectCount(wrapper.eq(User::getUserName, userName));
        return (rs > 0);
    }

    @Override
    public boolean saveBatch(Collection<User> entityList, int batchSize) {
        return false;
    }

    @Override
    public boolean saveOrUpdateBatch(Collection<User> entityList, int batchSize) {
        return false;
    }

    @Override
    public boolean updateBatchById(Collection<User> entityList, int batchSize) {
        return false;
    }

    @Override
    public boolean saveOrUpdate(User entity) {
        return false;
    }

    @Override
    public User getOne(Wrapper<User> queryWrapper, boolean throwEx) {
        return null;
    }

    @Override
    public Map<String, Object> getMap(Wrapper<User> queryWrapper) {
        return null;
    }

    @Override
    public <V> V getObj(Wrapper<User> queryWrapper, Function<? super Object, V> mapper) {
        return null;
    }

    @Override
    public BaseMapper<User> getBaseMapper() {
        return null;
    }

    @Override
    public Class<User> getEntityClass() {
        return null;
    }
}
